/*
 * Copyright 2007-2008 Michele Mostarda ( michele.mostarda@gmail.com ).
 * All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package com.asemantics.rdfcoder.model;

import com.asemantics.rdfcoder.model.java.JavaCodeHandler;
import com.asemantics.rdfcoder.model.java.JavaCodeModel;

import java.util.ArrayList;
import java.util.List;

/**
 * This class provides facility methods to read a <i>context</i>
 * built using the {@link com.asemantics.rdfcoder.model.IdentifierBuilder}.
 *
 * @see com.asemantics.rdfcoder.model.IdentifierBuilder
 * @version $Id: IdentifierReader.java 183 2010-09-12 21:32:09Z michele.mostarda $
 */
public class IdentifierReader {

    /**
     * Reads an identifier generated by the {@link com.asemantics.rdfcoder.model.IdentifierBuilder}.
     *
     * @param identifier
     * @return the parsed identifier.
     * @throws IllegalArgumentException if an error occurred during parsing.
     * @see com.asemantics.rdfcoder.model.IdentifierBuilder
     */
    public static Identifier readIdentifier(String identifier) {
        final int endPrefixIndex = identifier.indexOf(CodeModel.URI_PREFIX_SEPARATOR);
        final String prefix = identifier.substring(0, endPrefixIndex + 1);
        final String postfix = identifier.substring(endPrefixIndex + 1);
        final String[] sections = postfix.split("\\" + JavaCodeHandler.PACKAGE_SEPARATOR);
        IdentifierFragment[] fragments = new IdentifierFragment[sections.length];
        int i = 0;
        String[]  sectionSplit;
        String currentQualifier = null;
        for(String section : sections) {
            sectionSplit = section.split(CodeModel.PREFIX_SEPARATOR);
            if(section.lastIndexOf(CodeModel.PREFIX_SEPARATOR) == section.length() - 1) {
                final int sectionSplitLength = sectionSplit.length;
                String[] newSectionSplit = new String[sectionSplitLength + 1];
                System.arraycopy(sectionSplit, 0, newSectionSplit, 0, sectionSplitLength);
                newSectionSplit[newSectionSplit.length - 1] = "";
                sectionSplit = newSectionSplit;
            }
            if(sectionSplit.length > 2) {
                throw new IllegalArgumentException(
                        String.format("Invalid section [%s] in identifier [%s] ", section, identifier)
                );
            }
            if(sectionSplit.length == 2) {
                currentQualifier = sectionSplit[0];
                if(currentQualifier.length() == 0) {
                    throw new IllegalArgumentException(
                        String.format("Invalid 0 length section qualifier in identifier [%s]", identifier)
                    );
                }
            } else {
                if(currentQualifier == null) {
                    throw new IllegalArgumentException(
                        String.format("Invalid identifier [%s]: must start with a qualifier.", identifier)
                    );
                }
            }
            fragments[i] = new IdentifierFragment(
                    sectionSplit.length == 2 ? sectionSplit[1] : sectionSplit[0],
                    currentQualifier
            );
            i++;
        }
        return new Identifier(prefix, fragments);
    }

    /**
     * Reads a package and returns the representing identifier.
     *
     * @param pack the package to be read.
     * @return the parsed identifier.
     * @throws IllegalArgumentException if an error occurred during parsing.
     */
    public static Identifier readPackage(String pack) {
        if(pack.contains(CodeModel.PREFIX_SEPARATOR)) {
            throw new IllegalArgumentException("Invalid package: " + pack);
        }
        String[] sections = pack.split("\\" + JavaCodeHandler.PACKAGE_SEPARATOR);
        IdentifierFragment[] fragments = new IdentifierFragment[sections.length];
        int i = 0;
        for(String section : sections) {
            fragments[i] = new IdentifierFragment(section, JavaCodeModel.PACKAGE_KEY);
            i++;
        }
        return new Identifier(CodeModel.CODER_URI, fragments);
    }

    /**
     * Reads a fully qualified class.
     *
     * @param clazz the class name.
     * @return the identifier representing the class.
     * @throws IllegalArgumentException if an error occurred during parsing.
     */
    public static Identifier readFullyQualifiedClass(String clazz) {
        try {
            return readFullyQualifiedType(clazz, JavaCodeModel.CLASS_KEY);
        } catch (Exception e) {
            throw new IllegalArgumentException("Error while parsing class '" + clazz + "'", e);
        }
    }

    /**
     * Reads a fully qualified interface.
     *
     * @param iface the interface name.
     * @return the identifier representing the interface.
     * @throws IllegalArgumentException if an error occurred during parsing.
     */
    public static Identifier readFullyQualifiedInterface(String iface) {
        return readFullyQualifiedType(iface, JavaCodeModel.INTERFACE_KEY);
    }

    /**
     * Reads a fully qualified enumeration.
     *
     * @param enumeration the enumeration name.
     * @return the identifier representing the enumeration.
     * @throws IllegalArgumentException if an error occurred during parsing.
     */
    public static Identifier readFullyQualifiedEnumeration(String enumeration) {
        return readFullyQualifiedType(enumeration, JavaCodeModel.ENUMERATION_KEY);
    }

    /**
     * Reads a fully qualified field name.
     *
     * @param attribute the attribute name.
     * @return the identifier representing the attribute.
     * @throws IllegalArgumentException if an error occurred during parsing.
     */
    public static Identifier readFullyQualifiedAttribute(String attribute) {
        return readFullyQualifiedType(attribute, JavaCodeModel.CLASS_KEY, JavaCodeModel.ATTRIBUTE_KEY);
    }

    /**
     * Reads a fully qualified constructor name.
     *
     * @param constructor the constructor name.
     * @return the identifier representing the constructor.
     * @throws IllegalArgumentException if an error occurred during parsing.
     */
    public static Identifier readFullyQualifiedConstructor(String constructor) {
        return readFullyQualifiedType(constructor, JavaCodeModel.CONSTRUCTOR_KEY);
    }

    /**
     * Reads a fully qualified method name.
     *
     * @param method the method name.
     * @return the identifier representing the method.
     * @throws IllegalArgumentException if an error occurred during parsing.
     */
    public static Identifier readFullyQualifiedMethod(String method) {
        return readFullyQualifiedType(method, JavaCodeModel.CLASS_KEY, JavaCodeModel.METHOD_KEY);
    }

    /**
     * Reads a fully qualified type and return an identifier with the specified strongest qualifier. 
     *
     * @param pathToType string describeing the path to the type.
     * @param qualifier list of segment qualifiers.
     * @return the identifier generated.
     */
    private static Identifier readFullyQualifiedType(final String pathToType, final String... qualifier) {
        int packageEnd = -1;
        int fromIndex = pathToType.length();
        final String[] segments = new String[qualifier.length];
        for(int i = 0; i < qualifier.length; i++) {
            packageEnd = pathToType.lastIndexOf(JavaCodeHandler.PACKAGE_SEPARATOR, fromIndex - 1);
            if(packageEnd == -1) {
                break;
            }
            segments[segments.length - i - 1] = pathToType.substring(packageEnd + 1, fromIndex);
            fromIndex = packageEnd;
        }
        List<IdentifierFragment> fragments = new ArrayList<IdentifierFragment>();
        if(packageEnd == -1) {
            fragments.add( new IdentifierFragment("", JavaCodeModel.PACKAGE_KEY) );
        } else {
            Identifier packageIdentifier = IdentifierReader.readPackage(pathToType.substring(0, packageEnd));
            fragments.addAll( packageIdentifier.fragments );
        }
        for(int i = 0; i < qualifier.length; i++) {
            fragments.add( new IdentifierFragment(segments[i], qualifier[i]) );
        }
        return new Identifier(CodeModel.CODER_URI, fragments);
    }

}